"""
# Python中iterable、iterator和generator的区别与联系

# 可迭代对象（iterable）：是指可以直接作用于for循环的对象，如列表、元组、字符串、字典、集合等。这些对象实现了__iter__()方法，返回一个迭代器对象。
# 占用内存较多，因为需要将所有元素存储在内存中。
# 例如：列表、字符串、元组、字典、集合等。
# 定义一个列表，它是一个典型的可迭代对象
my_list = [10, 20, 30, 40]

# 直接使用for循环遍历可迭代对象
print("--- 遍历列表 (Iterable) ---")
for item in my_list:
    # 打印当前元素
    print(item)
# 输出: 10, 20, 30, 40

# 字符串也是可迭代对象
my_string = "Hello"
print("\n--- 遍历字符串 (Iterable) ---")
for char in my_string:
    # 打印当前字符
    print(char)
# 输出: H, e, l, l, o

# 迭代器（iterator）：是一个对象，它实现了__iter__()和__next__()方法。迭代器对象可以记住遍历的位置，每次调用next()方法返回下一个元素，直到没有元素时抛出StopIteration异常。
# 迭代器是惰性的，需要时才会生成下一个值，占用内存较少，因为它按需生成元素
# 使用iter()函数可以将可迭代对象转换为迭代器对象,使用内置的next方法可以获取下一个值。
# 将列表转换为迭代器对象
my_iterator = iter(my_list)
print("\n--- 遍历迭代器 (Iterator) ---")
while True:
    try:
        # 获取下一个元素
        item = next(my_iterator)
        # 打印当前元素
        print(item)
    except StopIteration:
        # 当没有更多元素时，退出循环
        break
# 输出: 10, 20, 30, 40
# 将字符串转换为迭代器对象
string_iterator = iter(my_string)
print("\n--- 遍历字符串迭代器 (Iterator) ---")
while True:
    try:
        # 获取下一个字符
        char = next(string_iterator)
        # 打印当前字符
        print(char)
    except StopIteration:
        # 当没有更多字符时，退出循环
        break


# 手动实现一个自定义迭代器
class CountIterator:
    def __init__(self, max):
        self.max = max
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.max:
            value = self.current
            self.current += 1
            return value
        else:
            raise StopIteration


# 使用自定义迭代器
count_iterator = CountIterator(5)
print("\n--- 自定义迭代器 (CountIterator) ---")
for number in count_iterator:
    # 打印当前数字
    print(number)
# 输出: 0, 1, 2, 3, 4
for number in count_iterator:
    # 打印当前数字
    print(number)


# 再手动实现一个接受列表的自定义迭代器
class ListIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        print("调用__next__方法，当前索引:", self.index)
        if self.index < len(self.data):
            value = self.data[self.index]
            self.index += 1
            return value
        else:
            print("迭代结束，抛出StopIteration异常")
            # 抛出的StopIteration会被for捕获，终止循环，但不会终止整个程序
            raise StopIteration


# 使用自定义列表迭代器
list_iterator = ListIterator(["a", "b", "c"])
print("\n--- 自定义列表迭代器 (ListIterator) ---")
for item in list_iterator:
    # 打印当前元素
    print(item)
# 输出: a, b, c
for item in list_iterator:
    # 打印当前元素
    print(item)
print("结束迭代")



# 生成器（generator）：是一种特殊的迭代器，使用yield语句生成值。生成器函数在调用时不会立即执行，而是返回一个生成器对象，只有在迭代时才会执行函数体并生成值。生成器也是惰性的，占用内存较少。
# 生成器函数定义了一个迭代器，每次调用next()时执行到下一个yield语句，返回yield后面的值。
# 定义一个生成器函数
def count_generator(max):
    count = 0
    while count < max:
        yield count
        count += 1


# 使用生成器函数
gen = count_generator(5)
print("\n--- 生成器 (Generator) ---")
for number in gen:
    # 打印当前数字
    print(number)
# 输出: 0, 1, 2, 3, 4
for number in gen:
    # 打印当前数字
    print(number)
print("结束生成器迭代")


# 生成器表达式
squares = (x * x for x in range(5))
print("\n--- 生成器表达式 (Generator Expression) ---")
for square in squares:
    print(square)
# 输出: 0, 1, 4, 9, 16
for square in squares:
    print(square)
print("结束生成器表达式迭代")


import sys

# 生成器表达式和列表推导式的区别
# 生成器在处理数据时有显著的内存优势，尤其是处理大数据集时。列表推导式会一次性生成整个列表，占用较多内存，而生成器表达式是惰性的，按需生成元素，占用内存较少。
# 例如，生成1到1000000的平方数列表和生成器：
# 列表推导式
squares_list = [x * x for x in range(1, 1000001)]
# 生成器表达式
squares_generator = (x * x for x in range(1, 1000001))
# 使用列表推导式会占用较多内存
print(f"列表推导式生成的平方数列表长度: {sys.getsizeof(squares_list)}")
# 使用生成器表达式占用较少内存
print(f"生成器表达式生成的平方数生成器类型: {sys.getsizeof(squares_generator)}")
# 访问生成器的前5个平方数
for _ in range(5):
    print(next(squares_generator))

"""


# 斐波那契数列生成器
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b


# 使用斐波那契生成器
fib_gen = fibonacci()
for _ in range(40):
    print(next(fib_gen))


# 使用尾递归实现斐波那契数列
def fibonacci_tail_recursive(n, a=0, b=1):
    if n == 0:
        return a
    else:
        return fibonacci_tail_recursive(n - 1, b, a + b)


# 计算前40个斐波那契数
for i in range(40):
    print(fibonacci_tail_recursive(i))

# 计算以上两种方式那种更快、占用内存更少
import time

start_time = time.time()
fib_gen = fibonacci()
for _ in range(40):
    next(fib_gen)
end_time = time.time()
print(f"生成器计算40个斐波那契数耗时: {end_time - start_time} 秒")
start_time = time.time()
for i in range(40):
    fibonacci_tail_recursive(i)
end_time = time.time()
print(f"尾递归计算40个斐波那契数耗时: {end_time - start_time} 秒")
import sys

fib_gen = fibonacci()
for _ in range(40):
    next(fib_gen)
print(f"生成器对象内存占用: {sys.getsizeof(fib_gen)} 字节")
print(f"尾递归函数调用栈内存占用: 估计较高，具体取决于递归深度")
# 结论：生成器在计算斐波那契数列时通常更快且占用内存更少，尤其是对于较大的n值。
